package com.maxlen.camunda.service.impl;

import com.alibaba.fastjson.JSON;
import com.maxlen.camunda.constant.CamundaConstant;
import com.maxlen.camunda.entity.BpmnEntity;
import com.maxlen.camunda.entity.CamundaFormEntity;
import com.maxlen.camunda.entity.dto.*;
import com.maxlen.camunda.entity.vo.*;
import com.maxlen.camunda.exception.CamundaException;
import com.maxlen.camunda.mapper.BpmnMapper;
import com.maxlen.camunda.mapper.FormMapper;
import com.maxlen.camunda.service.CamundaService;
import com.maxlen.camunda.utils.CamundaUtils;
import com.maxlen.camunda.utils.DateUtils;
import com.maxlen.camunda.utils.ProcessUserUtils;
import com.tcloudit.common.core.domain.R;
import com.tcloudit.common.core.web.domain.PageEntity;
import com.tcloudit.common.core.web.page.TableDataInfo;
import com.tcloudit.common.security.utils.SecurityUtils;
import com.tcloudit.system.api.RemoteRoleService;
import com.tcloudit.system.api.RemoteUserService;
import com.tcloudit.system.api.domain.CamundaUserPost.SelectUserVO;
import com.tcloudit.system.api.domain.CamundaUserPost.UserTO;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.form.CamundaFormRef;
import org.camunda.bpm.engine.form.StartFormData;
import org.camunda.bpm.engine.form.TaskFormData;
import org.camunda.bpm.engine.history.*;
import org.camunda.bpm.engine.impl.RepositoryServiceImpl;
import org.camunda.bpm.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.camunda.bpm.engine.impl.pvm.PvmTransition;
import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
import org.camunda.bpm.engine.repository.*;
import org.camunda.bpm.engine.repository.Resource;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Comment;
import org.camunda.bpm.engine.task.DelegationState;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.*;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperties;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperty;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CamundaServiceImpl implements CamundaService {

    @Autowired
    private BpmnMapper bpmnMapper;

    @Autowired
    private FormMapper formMapper;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private RemoteUserService remoteUserService;

    @Autowired
    private RemoteRoleService remoteRoleService;

    /**
     * 根据Key获取所有版本的流程定义列表
     * @param page 分页搜索参数
     * @return 结果
     */
    @Override
    public TableDataInfo getProcessDefinition(PageEntity<String> page) {
        if(!StringUtils.hasText(page.getKey())){
            return null;
        }

        List<ProcessDefinition> processDefinitionList = processEngine
                .getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKey(page.getKey())
                .orderByDeploymentTime().desc()
                .listPage(page.getPage() * page.getSize(), page.getSize());

        long count = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKey(page.getKey())
                .count();

        List<ProcessDefinitionVO> processDefinitionVOList = new ArrayList<>();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            ProcessDefinitionVO processDefinitionVO = new ProcessDefinitionVO();
            processDefinitionVO.setName(
                    processDefinition.getName() != null
                            ? processDefinition.getName()
                            : processDefinition.getKey());
            processDefinitionVO.setId(processDefinition.getId());
            processDefinitionVO.setVersionTag(processDefinition.getVersionTag());
            processDefinitionVO.setStatus(!processDefinition.isSuspended());
            processDefinitionVO.setVersion(processDefinition.getVersion());
            processDefinitionVO.setDeploymentId(processDefinition.getDeploymentId());
            processDefinitionVOList.add(processDefinitionVO);
        }

        TableDataInfo tableDataInfo = new TableDataInfo();
        tableDataInfo.setTotal(count);
        tableDataInfo.setRows(processDefinitionVOList);

        return tableDataInfo;
    }

    /**
     * 删除所有版本的流程定义
     *
     * @param processKey 流程定义Key
     */
    @Override
    @Transactional
    public void removeProcessDefinition(String[] processKey) {
        List<ProcessDefinition> list = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKeysIn(processKey)
                .list();
        for (ProcessDefinition processDefinition : list) {
            processEngine.getRepositoryService().deleteDeployment(processDefinition.getDeploymentId(), true);
        }
    }


    /**
     * 根据部署ID删除流程定义
     * @param deploymentId 流程定义ID
     * @return 结果
     */
    @Override
    public int removeProcessDefinitionByDeploymentId(String[] deploymentId) {
        for (String id : deploymentId) {
            processEngine.getRepositoryService().deleteDeployment(id, true);
        }
        return 1;
    }

    /**
     * 获取对应部署ID的BPMN模型
     * @param deploymentId 部署ID
     * @return 结果
     */
    @Override
    public String getProcessBpmn(String deploymentId) {
        List<Resource> resources = processEngine.getRepositoryService().getDeploymentResources(deploymentId);
        for (Resource resource : resources) {
            String[] split = resource.getName().split("\\.");
            if(split.length > 0){
                if(CamundaConstant.BPMN.equals(split[1])){
                    byte[] bytes = resource.getBytes();
                    return new String(bytes);
                }
            }
        }
        return null;
    }

    /**
     * 更新流程定义的状态
     * @param key 流程状态标识
     * @return 结果
     */
    @Override
    public int updateProcessStatus(String key) {
        List<ProcessDefinition> processDefinitionList = processEngine
                .getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKey(key)
                .latestVersion()
                .list();

        boolean isSuspended = true;
        for (ProcessDefinition processDefinition : processDefinitionList) {
            isSuspended = processDefinition.isSuspended();
        }

        UpdateProcessDefinitionSuspensionStateTenantBuilder builder = processEngine
                .getRepositoryService()
                .updateProcessDefinitionSuspensionState().byProcessDefinitionKey(key);

        if(isSuspended) {
            builder.activate();
        } else {
            builder.suspend();
        }

        return 1;
    }

    /**
     * 发布工作流程
     * @param id bpmn表 id
     */
    @Override
    public void createProcessDefinition(String id) throws Throwable {
        BpmnEntity info = bpmnMapper.info(id);
        Optional.ofNullable(info).orElseThrow((Supplier<Throwable>) () -> new CamundaException("流程不存在"));

        BpmnModelInstance modelInstance;
        DeploymentBuilder deploymentBuilder;
        try(ByteArrayInputStream inputStream = new ByteArrayInputStream(info.getBytes())) {
            modelInstance = Bpmn.readModelFromStream(inputStream);
            inputStream.reset();
            deploymentBuilder = processEngine.getRepositoryService().createDeployment()
                    .addInputStream(info.getBpmnId() + CamundaConstant.BPMN_SUFFIX, inputStream);
        } catch (IOException e){
            throw new CamundaException("服务器异常");
        }

        Set<String> formRef = new HashSet<>();
        String formSplit = CamundaConstant.FORM_SPLIT;

        // 获取用户任务表单
        Collection<UserTask> userTasks = modelInstance.getModelElementsByType(UserTask.class);
        for (UserTask userTask : userTasks) {
            String camundaFormRef = userTask.getCamundaFormRef();
            if(StringUtils.hasText(camundaFormRef)){
                formRef.add(camundaFormRef.split(formSplit)[0]);
            }
        }

        // 获取开始表单
        Collection<StartEvent> startEvents = modelInstance.getModelElementsByType(StartEvent.class);
        for (StartEvent startEvent : startEvents) {
            String camundaFormRef = startEvent.getCamundaFormRef();
            if(StringUtils.hasText(camundaFormRef)){
                formRef.add(camundaFormRef.split(formSplit)[0]);
            }
        }

        List<String> deploymentIdList = new ArrayList<>();
        if(formRef.size() > 0){
            deploymentIdList = formMapper.getDeploymentIdList(new ArrayList<>(formRef));
        }

        for (String deploymentId : deploymentIdList) {
            deploymentBuilder.addDeploymentResources(deploymentId);
        }
        deploymentBuilder.deploy();
    }

    /**
     * 获取流程定义监控列表
     * @param page 分页和检索
     * @return 结果
     */
    @Override
    public TableDataInfo getProcessControl(PageEntity<ProcessControlKeyDTO> page) {

        if(page.isEmpty()){
            throw new CamundaException("参数为空");
        }

        HistoricProcessInstanceQuery query = processEngine.getHistoryService()
                .createHistoricProcessInstanceQuery()
                .orderByProcessInstanceStartTime().desc();

        if(page.getKey() != null){
            String processStatus = page.getKey().getProcessStatus();
            if(StringUtils.hasText(processStatus)){
                if(CamundaConstant.COMPLETED.equals(processStatus)){
                    query.completed();
                }
                if(CamundaConstant.ACTIVE.equals(processStatus)){
                    query.active();
                }
                if(CamundaConstant.SUSPENDED.equals(processStatus)){
                    query.suspended();
                }
            }

            String startUserName = page.getKey().getStartUserName();
            if(StringUtils.hasText(startUserName)){
                query.startedBy(startUserName);
            }
        }

        List<HistoricProcessInstance> historicProcessInstances = query
                .listPage(page.getPage() * page.getSize(), page.getSize());

        List<ProcessControlVO> processControlVOList = new ArrayList<>();
        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
            ProcessControlVO processControlVO = new ProcessControlVO();
            processControlVO.setProcessName(
                    historicProcessInstance.getProcessDefinitionName() == null
                            ? historicProcessInstance.getProcessDefinitionKey()
                            : historicProcessInstance.getProcessDefinitionName());
            processControlVO.setProcessTitle(historicProcessInstance.getBusinessKey());
            processControlVO.setProcessStatus(historicProcessInstance.getState());
            processControlVO.setCreateTime(historicProcessInstance.getStartTime());
            processControlVO.setEndTime(historicProcessInstance.getEndTime());
            processControlVO.setStartUserName(historicProcessInstance.getStartUserId());
            processControlVO.setProcessInstanceId(historicProcessInstance.getId());
            processControlVOList.add(processControlVO);
        }

        TableDataInfo tableDataInfo = new TableDataInfo();
        tableDataInfo.setRows(processControlVOList);
        tableDataInfo.setTotal(processEngine.getHistoryService().createHistoricProcessInstanceQuery().count());
        return tableDataInfo;
    }

    /**
     * 删除流程定义监控
     * @param processInstanceIds 流程定义ID集合
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteProcessControl(String[] processInstanceIds){
        Set<String> stringSet = new HashSet<>(Arrays.asList(processInstanceIds));
        List<ProcessInstance> list = processEngine
                .getRuntimeService()
                .createProcessInstanceQuery()
                .processInstanceIds(stringSet).list();
        List<String> processInstanceIdList = list.stream().map(ProcessInstance::getProcessInstanceId).collect(Collectors.toList());
        processEngine.getRuntimeService().deleteProcessInstances(
                processInstanceIdList,
                null,
                false,
                false

        );
        processEngine.getHistoryService().deleteHistoricProcessInstances(Arrays.asList(processInstanceIds));
        return 1;
    }

    /**
     * 获取流程的表单
     * @param processInstanceId 流程定义ID
     * @return 结果
     */
    @Override
    public ProcessFormVO getProcessForm(String processInstanceId) {
        String joinUserPostName = getJoinUserPostName();

        ProcessFormVO processFormVO = new ProcessFormVO();
        processFormVO.setIsHandle(false);

        // 获取开始表单
        String processDefinitionId = processEngine.getHistoryService().createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult().getProcessDefinitionId();
        StartFormData startFormData = processEngine.getFormService().getStartFormData(processDefinitionId);
        String startFormKey = startFormData.getCamundaFormRef().getKey();
        InputStream resourceAsStream = processEngine.getRepositoryService()
                .getResourceAsStream(startFormData.getDeploymentId(), startFormKey + CamundaConstant.FORM_SUFFIX);
        processFormVO.setStartForm(readInputStream(resourceAsStream));

        // 获取流程中正在执行的任务的表单
        String taskDefinitionKey = null;
        List<Task> list = processEngine.getTaskService()
                .createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();
        for (Task task : list) {
            if(joinUserPostName.equals(task.getOwner())){
                processFormVO.setIsHandle(false);
                processFormVO.setTaskId(task.getId());
                processFormVO.setActivityName(null);
                processFormVO.setForm(null);
            }
            if(joinUserPostName.equals(task.getAssignee())){
                taskDefinitionKey = task.getTaskDefinitionKey();
                processFormVO.setIsHandle(true);
                processFormVO.setTaskId(task.getId());
                processFormVO.setActivityName(task.getName());
                TaskFormData taskFormData = processEngine.getFormService().getTaskFormData(task.getId());
                CamundaFormRef camundaFormRef = taskFormData.getCamundaFormRef();
                if(camundaFormRef != null){
                    InputStream taskInputStream = processEngine.getRepositoryService()
                            .getResourceAsStream(taskFormData.getDeploymentId(),
                                    taskFormData.getCamundaFormRef().getKey() + CamundaConstant.FORM_SUFFIX);
                    processFormVO.setForm(readInputStream(taskInputStream));
                }
            }
        }

        // 获取完成的节点的表单与参数
        List<ProcessFormVO.FinishedForm> finishedForms = new ArrayList<>();
        String deploymentId = processEngine.getRepositoryService().createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult()
                .getDeploymentId();
        List<HistoricTaskInstance> finishedTask = processEngine.getHistoryService().createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished()
                .orderByHistoricTaskInstanceEndTime().asc()
                .list();
        String activity = "";
        for (HistoricTaskInstance historicTaskInstance : finishedTask) {
            if(historicTaskInstance.getTaskDefinitionKey().equals(activity)){
                continue;
            }
            activity = historicTaskInstance.getTaskDefinitionKey();
            String definitionKey = historicTaskInstance.getTaskDefinitionKey();
            if(!definitionKey.equals(taskDefinitionKey)){
                ModelElementInstance modelElementById = processEngine.getRepositoryService().getBpmnModelInstance(processDefinitionId)
                        .getModelElementById(definitionKey);
                UserTask userTask = (UserTask) modelElementById;
                String camundaFormRef = userTask.getCamundaFormRef();
                if(StringUtils.hasText(camundaFormRef) && !camundaFormRef.equals(startFormKey)) {
                    ProcessFormVO.FinishedForm form = new ProcessFormVO.FinishedForm();
                    InputStream stream = processEngine.getRepositoryService()
                            .getResourceAsStream(deploymentId, camundaFormRef + CamundaConstant.FORM_SUFFIX);
                    form.setNodeName(historicTaskInstance.getName());
                    form.setForm(readInputStream(stream));
                    finishedForms.add(form);
                }
            }
        }
        processFormVO.setFinishedForms(finishedForms);


        // 获取表单参数
        List<HistoricVariableInstance> historicVariableInstances = processEngine.getHistoryService()
                .createHistoricVariableInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();

        Map<String, Object> map = new HashMap<>();
        for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {
            map.put(historicVariableInstance.getName(), historicVariableInstance.getValue());
        }
        processFormVO.setVariables(map);

        return processFormVO;
    }

    /**
     * 获取流程的审批意见
     * @param processInstanceId 流程定义ID
     * @return 结果
     */
    public List<ProcessCommentVO> getProcessComment(String processInstanceId){
        // 获取审核意见
        List<ProcessCommentVO> processCommentVOS = new ArrayList<>();
        List<HistoricTaskInstance> historicTaskInstanceList = processEngine.getHistoryService()
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();
        for (HistoricTaskInstance historicTaskInstance : historicTaskInstanceList) {
            List<Comment> taskComments = processEngine
                    .getTaskService()
                    .getTaskComments(historicTaskInstance.getId());
            for (Comment taskComment : taskComments) {
                ProcessCommentVO comments = new ProcessCommentVO();
                comments.setName(historicTaskInstance.getName());
                comments.setUser(taskComment.getUserId());

                String fullMessage = taskComment.getFullMessage();
                String[] split = fullMessage.split(CamundaConstant.OPINION_SPLIT);
                comments.setHandler(split[0]);

                comments.setResolution(fullMessage.replace(split[0] + ";", ""));
                comments.setEndTime(taskComment.getTime());
                processCommentVOS.add(comments);
            }
        }
        processCommentVOS.sort(Comparator.comparing(ProcessCommentVO::getEndTime));

        return processCommentVOS;
    }

    /**
     * 获取需要展示的流程图
     * @param processInstanceId 流程定义ID
     * @return 结果
     */
    @Override
    public ProcessBpmnVO getProcessShowBpmn(String processInstanceId) {
        ProcessBpmnVO processBpmnVO = new ProcessBpmnVO();
        HistoryService historyService = processEngine.getHistoryService();

        // 获取流程图与表单资源
        HistoricProcessInstance historicProcessInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if(historicProcessInstance == null) {return null;}

        String deploymentId = processEngine
                .getRepositoryService()
                .getProcessDefinition(historicProcessInstance.getProcessDefinitionId())
                .getDeploymentId();
        if(!StringUtils.hasText(deploymentId)) {return null;}

        List<Resource> deploymentResources = processEngine.getRepositoryService().getDeploymentResources(deploymentId);
        for (Resource deploymentResource : deploymentResources) {
            String[] split = deploymentResource.getName().split("\\.");
            if(CamundaConstant.BPMN.equals(split[split.length - 1])){
                processBpmnVO.setBpmn(new String(deploymentResource.getBytes()));
            }
        }

        // 获取完成的节点
        List<HistoricActivityInstance> finished = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished().list();
        Set<String> finishPoint = new HashSet<>();
        for (HistoricActivityInstance historicActivityInstance : finished) {
            finishPoint.add(historicActivityInstance.getActivityId());
        }

        // 获取待完成的节点
        List<HistoricActivityInstance> unFinished = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .unfinished().list();
        Set<String> waitPoint = new HashSet<>();

        // 获取有当前用户需完成的节点
        Set<String> doPoint = new HashSet<>();
        String joinUserPostName = getJoinUserPostName();
        for (HistoricActivityInstance activityInstance : unFinished) {
            if(joinUserPostName.equals(activityInstance.getAssignee())){
                doPoint.add(activityInstance.getActivityId());
                waitPoint.remove(activityInstance.getActivityId());
            } else {
                if(!doPoint.contains(activityInstance.getActivityId())){
                    waitPoint.add(activityInstance.getActivityId());
                }
            }
        }

        processBpmnVO.setFinishPoint(finishPoint);
        processBpmnVO.setWaitPoint(waitPoint);
        processBpmnVO.setDoPoint(doPoint);
        return processBpmnVO;
    }

    /**
     * 获取当前部署流程的节点的权限
     * @param taskId 任务ID
     * @return 结果
     */
    @Override
    public List<String> getProcessAuth(String taskId) {
        String joinUserPostName = getJoinUserPostName();
        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
        List<String> authList = new ArrayList<>();

        if(joinUserPostName.equals(task.getAssignee())){
            authList = new ArrayList<>(getCurrentNodeExtensionProperty(task, CamundaConstant.AUTH));

            // 判断是普通任务还是串行或并行任务
            if(!isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey())){
                authList.remove(CamundaConstant.ADD_SIGN);
                authList.remove(CamundaConstant.DELETE_SIGN);
            }

            // 判断是否是委托任务
            DelegationState delegationState = task.getDelegationState();
            if(delegationState != null && CamundaConstant.PENDING.equals(delegationState.name())){
                if(!task.getOwner().equals(task.getAssignee())){
                    authList.remove(CamundaConstant.DELEGATE);
                    authList.add(CamundaConstant.COMPLETE_DELEGATE);
                }
            }
        }
        DelegationState delegationState = task.getDelegationState();
        if(
                delegationState != null && CamundaConstant.PENDING.equals(delegationState.name()) &&
                task.getOwner().equals(joinUserPostName)){
            authList.add(CamundaConstant.CANCEL_DELEGATE);
        }

        return authList;
    }

    /**
     * 完成任务
     * @param taskCompleteDTO 参数
     * @return R
     */
    @Override
    public int completeTask(TaskCompleteDTO taskCompleteDTO) throws RuntimeException{
        String taskId = taskCompleteDTO.getTaskId();
        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();

        judgeNextNodeAssigneeExist(taskCompleteDTO.getMap(), task);

        Map<String, Object> variables = processEngine.getRuntimeService().getVariables(task.getExecutionId());
        if(!returnStartNode(task, taskCompleteDTO.getOption(), taskCompleteDTO.getOpinion(), taskCompleteDTO.getMap())){
            variables.putAll(taskCompleteDTO.getMap());
            processEngine.getTaskService().complete(taskId, variables);
            String full = CamundaConstant.ERROR;
            if(taskCompleteDTO.getOption()){
                full = CamundaConstant.SUCCESS;
            }
            addOpinion(
                    taskId,
                    task.getProcessInstanceId(),
                    full + CamundaConstant.OPINION_SPLIT + taskCompleteDTO.getOpinion());
        }

        return 1;
    }

    /**
     * 加签
     * @param signDTO 加签请求参数
     * @return 结果
     */
    @Override
    public int addSign(SignDTO signDTO) throws RuntimeException{
        String taskId = signDTO.getTaskId();
        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();

        // 参数与权限判断
        isAttrRight(signDTO, task, CamundaConstant.ADD_SIGN);

        List<String> userId = signDTO.getUserId();

        String taskDefinitionKey = task.getTaskDefinitionKey();
        String processDefinitionId = task.getProcessDefinitionId();
        String processInstanceId = task.getProcessInstanceId();

        if(isMultiInstanceSequential(processDefinitionId, taskDefinitionKey)){
            Map<String, Object> variables = processEngine.getRuntimeService().getVariables(task.getExecutionId());

            Object object = variables.get(CamundaConstant.ASSIGNEE_LIST);
            List<String> assigneeList = CamundaUtils.ObjectChangeArrays(object);
            assigneeList.addAll(userId);
            int nrOfInstance = (Integer) variables.get(CamundaConstant.NR_OF_INSTANCES) + userId.size();

            variables.put(CamundaConstant.ASSIGNEE_LIST, assigneeList);
            variables.put(CamundaConstant.NR_OF_INSTANCES, nrOfInstance);

            processEngine.getRuntimeService().setVariables(task.getExecutionId(), variables);
        } else {
            for (String user : userId) {
                processEngine.getRuntimeService()
                        .createProcessInstanceModification(processInstanceId)
                        .startBeforeActivity(taskDefinitionKey)
                        .setVariable(CamundaConstant.ASSIGNEE, user)
                        .execute();
            }
        }

        addOpinion(
                taskId,
                processInstanceId,
                CamundaConstant.ADD_SIGN + CamundaConstant.OPINION_SPLIT + signDTO.getOpinion());
        return 1;
    }

    /**
     * 获取加签的选人列表
     * @param taskId 任务ID
     * @return 结果
     */
    @Override
    public List<SelectUserVO> selectAddSignUser(String taskId) throws RuntimeException{
        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();

        List<String> assigneeList = new ArrayList<>();
        if(isMultiInstanceSequential(task.getProcessDefinitionId(), task.getTaskDefinitionKey())){
            Object variable = processEngine.getRuntimeService().getVariable(task.getExecutionId(), CamundaConstant.ASSIGNEE_LIST);
            if(variable instanceof ArrayList<?>){
                for (Object o : (List<?>) variable) {
                    assigneeList.add(o.toString());
                }
            }
            assigneeList.add(task.getAssignee().split(CamundaConstant.USER_DEPT_SPLIT)[0]);
        } else {
            List<Task> list = processEngine
                    .getTaskService()
                    .createTaskQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .list();
            assigneeList = list.stream().map(Task::getAssignee).collect(Collectors.toList());
        }

        List<String> userIds = new ArrayList<>();
        for (String assignee : assigneeList) {
            userIds.add(assignee.split(CamundaConstant.USER_DEPT_SPLIT)[0]);
        }

        com.tcloudit.common.core.domain.R<List<SelectUserVO>> selectUser = remoteUserService.getSelectUserNotInId(userIds);
        if(com.tcloudit.common.core.domain.R.isError(selectUser)){
            throw new RuntimeException("用户服务异常");
        }
        return selectUser.getData();
    }

    /**
     * 获取办理的选人列表
     * @param taskId 任务ID
     * @return 结果
     */
    @Override
    public SelectCompleteUserVO selectCompleteUser(String taskId) {

        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();

        List<String> nextNodeAssigneeList = getFlowNextNode(task);
        if(nextNodeAssigneeList != null){
             return null;
        }

        com.tcloudit.common.core.domain.R<List<SelectUserVO>> selectUser = remoteUserService.getSelectUserNotInId(new ArrayList<>());
        if(com.tcloudit.common.core.domain.R.isError(selectUser)){
            throw new RuntimeException("用户服务异常");
        }
        List<SelectUserVO> data = selectUser.getData();

        SelectCompleteUserVO vo = new SelectCompleteUserVO();

        String executionId = task.getExecutionId();
        Object variable = processEngine.getRuntimeService().getVariable(executionId, CamundaConstant.ASSIGNEE_LIST);
        List<String> assigneeList = CamundaUtils.ObjectChangeArrays(variable);

        List<String> userIdList = assigneeList.stream().map((item) -> item.split(CamundaConstant.USER_DEPT_SPLIT)[0])
                .collect(Collectors.toList());

        List<SelectUserVO> checkList = new ArrayList<>();
        for (SelectUserVO item : data) {
            if(userIdList.contains(item.getUserId().toString())){
                checkList.add(item);
            }
        }

        vo.setSelectUserList(data);
        vo.setCheckSelectUserList(checkList);
        return vo;
    }

    /**
     * 获取减签的选人列表
     * @param taskId 任务ID
     * @return 结果
     */
    @Override
    public List<SelectUserVO> selectDeleteSignUser(String taskId) throws RuntimeException{
        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();

        List<String> assigneeList = new ArrayList<>();
        if(isMultiInstanceSequential(task.getProcessDefinitionId(), task.getTaskDefinitionKey())){
            Map<String, Object> variables = processEngine.getRuntimeService().getVariables(task.getProcessInstanceId());

            Object object = variables.get(CamundaConstant.ASSIGNEE_LIST);
            List<String> oldAssigneeList = CamundaUtils.ObjectChangeArrays(object);
            boolean can = false;
            for (String old : oldAssigneeList) {
                if(can){
                    assigneeList.add(old);
                }
                if(task.getAssignee().equals(old)){
                    can = true;
                }
            }
        } else {
            List<Task> taskList = processEngine
                    .getTaskService()
                    .createTaskQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .list();
            assigneeList = taskList.stream().map(Task::getAssignee).collect(Collectors.toList());
        }

        List<String> userIds = new ArrayList<>();
        String userId = task.getAssignee().split(CamundaConstant.USER_DEPT_SPLIT)[0];
        for (String assignee : assigneeList) {
            String id = assignee.split(CamundaConstant.USER_DEPT_SPLIT)[0];
            if(!userId.equals(id)){
                userIds.add(id);
            }
        }

        if(userIds.isEmpty()){
            return null;
        }
        com.tcloudit.common.core.domain.R<List<SelectUserVO>> selectUser = remoteUserService.getSelectUserInId(userIds);
        if(com.tcloudit.common.core.domain.R.isError(selectUser)){
            throw new RuntimeException("用户服务异常");
        }
        return selectUser.getData();
    }

    /**
     * 减签
     * @param signDTO 参数
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteSign(SignDTO signDTO) throws RuntimeException{
        List<String> userIdList = signDTO.getUserId();
        if(userIdList.isEmpty()){
            throw new RuntimeException("未选择用户");
        }

        String taskId = signDTO.getTaskId();
        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();

        // 参数与权限判断
        isAttrRight(signDTO, task, CamundaConstant.DELETE_SIGN);

        String executionId = task.getExecutionId();
        Map<String, Object> variables = processEngine.getRuntimeService().getVariables(executionId);
        if(isMultiInstanceSequential(task.getProcessDefinitionId(), task.getTaskDefinitionKey())){
            Object object = variables.get(CamundaConstant.ASSIGNEE_LIST);
            List<String> assigneeList = CamundaUtils.ObjectChangeArrays(object);
            if(assigneeList.isEmpty()){
                throw new RuntimeException("任务不存在");
            }

            List<String> newAssigneeList = new ArrayList<>();
            for (String assignee : assigneeList) {
                if(assignee.equals(task.getAssignee())){
                    newAssigneeList.add(assignee);
                    continue;
                }
                boolean can = true;
                for (String userId : userIdList) {
                    if(assignee.equals(userId)){
                        can = false;
                    }
                }
                if(can){
                   newAssigneeList.add(assignee);
                }
            }

            variables.put(CamundaConstant.ASSIGNEE_LIST, newAssigneeList);
            if(newAssigneeList.size() == 0){
                variables.put(CamundaConstant.NR_OF_INSTANCES, 1);
            } else {
                variables.put(CamundaConstant.NR_OF_INSTANCES, newAssigneeList.size());
            }
            processEngine.getRuntimeService().setVariables(executionId, variables);
        } else {
            List<Task> taskList = processEngine
                    .getTaskService()
                    .createTaskQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .list();

            int oldNrOfCompleteInstance = (int) variables.get(CamundaConstant.NR_OF_COMPLETED_INSTANCES);
            variables.put(CamundaConstant.NR_OF_COMPLETED_INSTANCES, oldNrOfCompleteInstance - userIdList.size());
            processEngine.getRuntimeService().setVariables(executionId, variables);
            for (Task tasks : taskList) {
                if(!Objects.equals(task.getId(), tasks.getId())){
                    if(userIdList.contains(tasks.getAssignee())) {
                        processEngine.getTaskService().complete(tasks.getId());
                    }
                }
            }

            variables.put(CamundaConstant.NR_OF_COMPLETED_INSTANCES, oldNrOfCompleteInstance);
            processEngine.getRuntimeService().setVariables(executionId, variables);
        }

        addOpinion(
                taskId,
                task.getProcessInstanceId(),
                CamundaConstant.DELETE_SIGN + CamundaConstant.OPINION_SPLIT + signDTO.getOpinion());
        return 1;
    }

    /**
     * 委托
     * @param delegateDTO 参数
     * @return 结果
     */
    @Override
    @Transactional
    public int delegate(DelegateDTO delegateDTO) throws RuntimeException{
        String taskId = delegateDTO.getTaskId();
        String userId = delegateDTO.getUserId();
        if(!StringUtils.hasText(taskId) || !StringUtils.hasText(userId)){
            throw new RuntimeException("参数为空");
        }

        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String assignee = task.getAssignee();
        if(assignee.equals(userId)){
            throw new RuntimeException("委托用户重复");
        }

        String owner = task.getOwner();
        if(StringUtils.hasText(owner)){
            if(!owner.equals(assignee)){
                throw new RuntimeException("已有委托用户");
            }
        }

        taskService.delegateTask(taskId, userId);
        addOpinion(
                taskId,
                task.getProcessInstanceId(),
                CamundaConstant.DELEGATE + CamundaConstant.OPINION_SPLIT + delegateDTO.getOpinion());
        return 1;
    }

    /**
     * 解决委托任务
     * @param taskCompleteDTO 任务完成参数
     * @return 结果
     */
    @Override
    public int completeDelegate(TaskCompleteDTO taskCompleteDTO) {
        String taskId = taskCompleteDTO.getTaskId();
        if(!StringUtils.hasText(taskId)){
            throw new RuntimeException("参数异常");
        }

        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        DelegationState delegationState = task.getDelegationState();
        if(delegationState == null || !CamundaConstant.PENDING.equals(delegationState.name())){
            throw new RuntimeException("委托被取消");
        }

        taskService.resolveTask(taskId, taskCompleteDTO.getMap());
        addOpinion(
                taskId,
                task.getProcessInstanceId(),
                CamundaConstant.COMPLETE_DELEGATE + CamundaConstant.OPINION_SPLIT + taskCompleteDTO.getOpinion());
        return 1;
    }

    /**
     * 取消委托任务
     * @param opinionDTO 任务ID、意见
     * @return 结果
     */
    @Override
    public int cancelDelegate(OpinionDTO opinionDTO) {
        String taskId = opinionDTO.getTaskId();
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        String owner = task.getOwner();
        if(!owner.equals(getJoinUserPostName())){
            throw new RuntimeException("没有权限取消委托");
        }
        DelegationState delegationState = task.getDelegationState();
        if(delegationState == null || !CamundaConstant.PENDING.equals(delegationState.name())){
            throw new RuntimeException("没有委托用户");
        }

        taskService.resolveTask(taskId);
        addOpinion(
                taskId,
                task.getProcessInstanceId(),
                CamundaConstant.CANCEL_DELEGATE + CamundaConstant.OPINION_SPLIT + opinionDTO.getOpinion());
        return 1;
    }

    /**
     * 转办
     * @param delegateDTO 任务ID、用户ID、意见
     * @return 结果
     */
    @Override
    public int relayComplete(DelegateDTO delegateDTO) {
        String taskId = delegateDTO.getTaskId();
        if(taskId == null || delegateDTO.getUserId() == null){
            return 0;
        }

        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
        processEngine.getTaskService().setAssignee(taskId, delegateDTO.getUserId());
        addOpinion(
                taskId,
                task.getProcessInstanceId(),
                CamundaConstant.RELAY_COMPLETE + CamundaConstant.OPINION_SPLIT + delegateDTO.getOpinion());
        return 1;
    }

    /**
     * 退回到上一步的任务
     * @param opinionDTO 任务ID、意见
     * @return 结果
     */
    @Override
    public int returnBackTask(OpinionDTO opinionDTO) {
        String taskId = opinionDTO.getTaskId();
        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
        String taskDefinitionKey = task.getTaskDefinitionKey();
        String processDefinitionId = task.getProcessDefinitionId();

        List<HistoricActivityInstance> instances = processEngine.getHistoryService().createHistoricActivityInstanceQuery()
                .activityType(CamundaConstant.USER_TASK)
                .processInstanceId(task.getProcessInstanceId())
                .finished()
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .list();

        LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap<>();
        for(HistoricActivityInstance activityInstance : instances){
            linkedHashMap.put(activityInstance.getActivityId(),activityInstance.getAssignee());
        }

        String returnNodeId = null;
        String returnNodeAssignee = null;
        for (Map.Entry<String, String> entry : linkedHashMap.entrySet()) {
            if(entry.getKey().equals(taskDefinitionKey)){
                break;
            } else {
                returnNodeId = entry.getKey();
                returnNodeAssignee = entry.getValue();
            }
        }

        if(returnNodeId == null || returnNodeAssignee == null){
            throw new RuntimeException("第一个任务无法退回");
        }


        if(isMultiInstance(processDefinitionId, taskDefinitionKey)){
            taskDefinitionKey = taskDefinitionKey + CamundaConstant.REPLACE_MULTI_INSTANCE_BODY;
        }
        if(isMultiInstance(processDefinitionId, returnNodeId)){
            returnNodeId = returnNodeId + CamundaConstant.REPLACE_MULTI_INSTANCE_BODY;
        }
        processEngine.getRuntimeService().createProcessInstanceModification(task.getProcessInstanceId())
                .cancelAllForActivity(taskDefinitionKey)
                .startBeforeActivity(returnNodeId)
                .setVariable(CamundaConstant.ASSIGNEE, returnNodeAssignee)
                .execute();
        addOpinion(
                taskId,
                task.getProcessInstanceId(),
                CamundaConstant.RETURN_BACK + CamundaConstant.OPINION_SPLIT + opinionDTO.getOpinion());
        return 1;
    }

    /**
     * 设计模板页面，获取选人列表
     * @param choseDTO 已选中的用户
     * @return 结果
     */
    @Override
    public SelectCompleteUserVO getChoseUser(ChoseDTO choseDTO) {
        com.tcloudit.common.core.domain.R<List<SelectUserVO>> selectUser = remoteUserService.getSelectUserNotInId(new ArrayList<>());
        if(com.tcloudit.common.core.domain.R.isError(selectUser)){
            throw new RuntimeException("用户服务异常");
        }
        List<SelectUserVO> data = selectUser.getData();

        SelectCompleteUserVO vo = new SelectCompleteUserVO();
        List<SelectUserVO> checkList = new ArrayList<>();
        if(StringUtils.hasText(choseDTO.getChose())){
            String[] split = choseDTO.getChose().split(CamundaConstant.ASSIGNEE_SPLIT);
            List<String> userIdList = Arrays.stream(split).map((item) -> {
                return item.split(CamundaConstant.USER_DEPT_SPLIT)[0];
            }).collect(Collectors.toList());

            for (SelectUserVO item : data) {
                if(userIdList.contains(item.getUserId().toString())){
                    checkList.add(item);
                }
            }
        }

        vo.setSelectUserList(data);
        vo.setCheckSelectUserList(checkList);
        return vo;
    }

    /**
     * 设计模板页面，获取权限列表
     * @param choseDTO 已选中权限
     * @return 结果
     */
    @Override
    public AuthChoseVO getChoseAuth(ChoseDTO choseDTO) {
        AuthChoseVO authChoseVO = new AuthChoseVO();
        List<AuthChoseVO.AuthChose> authChoseList = new ArrayList<>();
        authChoseList.add(new AuthChoseVO.AuthChose(CamundaConstant.ADD_SIGN, "添加更多签名"));
        authChoseList.add(new AuthChoseVO.AuthChose(CamundaConstant.DELETE_SIGN, "减少签名"));
        authChoseList.add(new AuthChoseVO.AuthChose(CamundaConstant.DELEGATE, "将任务委托给其他人"));
        authChoseList.add(new AuthChoseVO.AuthChose(CamundaConstant.RELAY_COMPLETE, "将任务转发给其他人"));

        List<AuthChoseVO.AuthChose> checkAuthChoseList = new ArrayList<>();
        String[] split = choseDTO.getChose().split(CamundaConstant.ASSIGNEE_SPLIT);
        for (String auth : split) {
            if(CamundaConstant.ADD_SIGN.equals(auth)){
                checkAuthChoseList.add(new AuthChoseVO.AuthChose(CamundaConstant.ADD_SIGN, "添加更多签名"));
            }
            if(CamundaConstant.DELETE_SIGN.equals(auth)){
                checkAuthChoseList.add(new AuthChoseVO.AuthChose(CamundaConstant.DELETE_SIGN, "减少签名"));
            }
            if(CamundaConstant.DELEGATE.equals(auth)){
                checkAuthChoseList.add(new AuthChoseVO.AuthChose(CamundaConstant.DELEGATE, "将任务委托给其他人"));
            }
            if(CamundaConstant.RELAY_COMPLETE.equals(auth)){
                checkAuthChoseList.add(new AuthChoseVO.AuthChose(CamundaConstant.RELAY_COMPLETE, "将任务转发给其他人"));
            }
        }

        authChoseVO.setAuthChoseList(authChoseList);
        authChoseVO.setCheckAuthChoseList(checkAuthChoseList);
        return authChoseVO;
    }

    /**
     * 设计模板页面，获取表达式列表
     * @return 结果
     */
    @Override
    public List<ConditionChoseVO> getChoseCondition() {
        List<ConditionChoseVO> conditions = new ArrayList<>();
        conditions.add(new ConditionChoseVO(CamundaConstant.SUCCESS_ATTR, "同意人数"));
        conditions.add(new ConditionChoseVO(CamundaConstant.ERROR_ATTR, "拒绝人数"));
        conditions.add(new ConditionChoseVO(CamundaConstant.NR_OF_INSTANCES, "总审批人数"));
        conditions.add(new ConditionChoseVO(CamundaConstant.NR_OF_COMPLETED_INSTANCES, "审批完成的人数"));
        return conditions;
    }

    /**
     * 设计模板页面，根据表单标识获取表单中的变量
     * @param formList 表单标识集合
     * @return 结果
     */
    @Override
    public List<CamundaFormEntity.Components> getFormVariable(List<String> formList) {
        if(formList.isEmpty()){return new ArrayList<>();}

        List<CamundaFormEntity.Components> components = new ArrayList<>();
        List<String> deploymentIdList = formMapper.getDeploymentIdList(formList);
        for (String deploymentId : deploymentIdList) {
            List<Resource> resourceList = processEngine.getRepositoryService().getDeploymentResources(deploymentId);
            for (Resource resource : resourceList) {
                CamundaFormEntity camundaFormEntity = JSON.parseObject(new String(resource.getBytes()), CamundaFormEntity.class);
                components.addAll(camundaFormEntity.getComponents());
            }
        }

        return components;
    }

    /**
     * 获取用户任务列表
     * @return 结果
     */
    @Override
    public TableDataInfo getUserTask(PageEntity<ProcessControlKeyDTO> page) {
        if(page.isEmpty()){
            return null;
        }

        ProcessControlKeyDTO key = page.getKey();
        if(key == null){
            key = new ProcessControlKeyDTO();
        }
        key.setStartUserName(getJoinUserPostName());
        page.setKey(key);
        return getProcessControl(page);
    }

    /**
     * 根据当前用户获取用户任务列表
     * @param page 分页与检索
     * @return 结果
     */
    @Override
    public TableDataInfo getUserTaskByUser(PageEntity<ProcessControlKeyDTO> page) {
        if(page.isEmpty()){
            return null;
        }

        List<Task> tasks = processEngine.getTaskService().createTaskQuery()
                .taskAssignee(getJoinUserPostName())
                .orderByTaskCreateTime().desc()
                .listPage(page.getPage() * page.getSize(), page.getSize());

        List<UserTaskVO> userTaskVOS = new ArrayList<>();
        for (Task task : tasks) {
            UserTaskVO userTaskVO = new UserTaskVO();
            userTaskVO.setDelegate(task.getOwner());
            userTaskVO.setCurrentNode(task.getName());
            userTaskVO.setCreateTime(task.getCreateTime());
            HistoricProcessInstance historicProcessInstance = processEngine.getHistoryService()
                    .createHistoricProcessInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .singleResult();
            userTaskVO.setProcessName(
                    historicProcessInstance.getProcessDefinitionName() == null
                            ? historicProcessInstance.getProcessDefinitionKey()
                            : historicProcessInstance.getProcessDefinitionName());
            userTaskVO.setProcessTitle(historicProcessInstance.getBusinessKey());
            userTaskVO.setProcessInstanceId(task.getProcessInstanceId());
            userTaskVOS.add(userTaskVO);
        }

        TableDataInfo tableDataInfo = new TableDataInfo();
        tableDataInfo.setRows(userTaskVOS);
        tableDataInfo.setTotal(processEngine
                .getTaskService()
                .createTaskQuery()
                .taskAssignee(getJoinUserPostName())
                .count());
        return tableDataInfo;
    }

    /**
     * 根据当前用户获取发出委托的用户任务列表
     * @param page 分页与检索
     * @return 结果
     */
    @Override
    public TableDataInfo getDelegateTaskByUser(PageEntity<ProcessControlKeyDTO> page) {
        if(page.isEmpty()){
            return null;
        }

        List<Task> tasks = processEngine.getTaskService().createTaskQuery()
                .taskOwner(getJoinUserPostName())
                .orderByTaskCreateTime().desc()
                .listPage(page.getPage() * page.getSize(), page.getSize());

        List<UserTaskVO> userTaskVOS = new ArrayList<>();
        for (Task task : tasks) {
            if(!task.getAssignee().equals(task.getOwner())){
                UserTaskVO userTaskVO = new UserTaskVO();
                userTaskVO.setDelegate(task.getAssignee());
                userTaskVO.setCurrentNode(task.getName());
                userTaskVO.setCreateTime(task.getCreateTime());
                HistoricProcessInstance historicProcessInstance = processEngine.getHistoryService()
                        .createHistoricProcessInstanceQuery()
                        .processInstanceId(task.getProcessInstanceId())
                        .singleResult();
                userTaskVO.setProcessName(
                        historicProcessInstance.getProcessDefinitionName() == null
                                ? historicProcessInstance.getProcessDefinitionKey()
                                : historicProcessInstance.getProcessDefinitionName());
                userTaskVO.setProcessTitle(historicProcessInstance.getBusinessKey());
                userTaskVO.setProcessInstanceId(task.getProcessInstanceId());
                userTaskVOS.add(userTaskVO);
            }
        }

        TableDataInfo tableDataInfo = new TableDataInfo();
        tableDataInfo.setRows(userTaskVOS);
        tableDataInfo.setTotal(processEngine
                .getTaskService()
                .createTaskQuery()
                .taskAssignee(getJoinUserPostName())
                .count());
        return tableDataInfo;
    }

    /**
     * 根据当前用户获取具有的流程
     * @return 结果
     */
    @Override
    public List<ProcessByUserIdVO> getProcessByUserId() {
        R<List<String>> result = remoteRoleService.selectUserIdAuth(SecurityUtils.getUserId().toString());
        if(R.isError(result)){
            return new ArrayList<>();
        }

        List<String> data = result.getData();
        List<ProcessByUserIdVO> processByUserIdVOS = new ArrayList<>();
        for (String datum : data) {
            List<ProcessDefinition> list = processEngine.getRepositoryService()
                    .createProcessDefinitionQuery()
                    .active()
                    .orderByProcessDefinitionVersion().desc()
                    .processDefinitionKey(datum).list();

            if(list.size() > 0){
                ProcessDefinition processDefinition = processEngine.getRepositoryService()
                        .createProcessDefinitionQuery()
                        .processDefinitionKey(datum)
                        .latestVersion()
                        .singleResult();

                ProcessByUserIdVO process = new ProcessByUserIdVO();
                process.setProcessKey(processDefinition.getKey());
                process.setProcessTitle(processDefinition.getName());

                List<ProcessByUserIdVO.Version> versions = new ArrayList<>();
                for (ProcessDefinition definition : list) {
                    versions.add(new ProcessByUserIdVO.Version(definition.getVersion(), definition.getVersionTag()));
                }
                process.setVersion(versions);
                processByUserIdVOS.add(process);
            }
        }


        return processByUserIdVOS;
    }

    /**
     * 获取流程的开始表单
     * @param processVersionDTO 流程Key与版本
     * @return 结果
     */
    @Override
    public String getProcessStartForm(ProcessVersionDTO processVersionDTO) {
        if(processVersionDTO.getProcessKey() == null || processVersionDTO.getVersion() == null){
            return null;
        }
        ProcessDefinition processDefinition = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKey(processVersionDTO.getProcessKey())
                .processDefinitionVersion(processVersionDTO.getVersion())
                .singleResult();

        StartFormData startFormData = processEngine.getFormService().getStartFormData(processDefinition.getId());
        if(startFormData == null || startFormData.getCamundaFormRef() == null){
            return null;
        }

        InputStream resourceAsStream = processEngine
                .getRepositoryService()
                .getResourceAsStream(startFormData.getDeploymentId(),
                        startFormData.getCamundaFormRef().getKey() + CamundaConstant.FORM_SUFFIX);
        return readInputStream(resourceAsStream);
    }

    /**
     * 根据流程Key和版本号获取流程模型
     * @param processVersionDTO 流程Key与版本号
     * @return 结果
     */
    @Override
    public String getProcessBpmnByVersion(ProcessVersionDTO processVersionDTO) {
        if(processVersionDTO.getProcessKey() == null || processVersionDTO.getVersion() == null){
            return null;
        }
        ProcessDefinition processDefinition = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKey(processVersionDTO.getProcessKey())
                .processDefinitionVersion(processVersionDTO.getVersion())
                .singleResult();

        InputStream bpmn = processEngine
                .getRepositoryService()
                .getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        return readInputStream(bpmn);
    }

    /**
     * 根据流程Key和版本号开启流程
     * @param startProcessVersionDTO 流程Key与版本
     * @return 结果
     */
    @Override
    public ProcessInstanceTitleVO startProcessByVersion(StartProcessVersionDTO startProcessVersionDTO) {
        if(startProcessVersionDTO.getProcessKey() == null || startProcessVersionDTO.getVersion() == null){
            return null;
        }
        ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery()
                .processDefinitionKey(startProcessVersionDTO.getProcessKey())
                .processDefinitionVersion(startProcessVersionDTO.getVersion()).singleResult();
        if(processDefinition.isSuspended()){
            throw new CamundaException("流程已挂起");
        }

        boolean full = judgeFirstUserTaskAssigneeFull(processDefinition.getId());
        if(!full){
            Object o = startProcessVersionDTO.getMap().get(CamundaConstant.ASSIGNEE_LIST);
            List<String> assigneeList = CamundaUtils.ObjectChangeArrays(o);
            if(assigneeList.isEmpty()){
                throw new CamundaException("未添加分配人");
            }
        }else{
            startProcessVersionDTO.getMap().remove(CamundaConstant.ASSIGNEE_LIST);
        }

        processEngine.getIdentityService().setAuthenticatedUserId(getJoinUserPostName());
        ProcessInstance processInstance = processEngine.getRuntimeService()
                .startProcessInstanceById(
                        processDefinition.getId(),
                        startProcessVersionDTO.getBusinessKey(),
                        startProcessVersionDTO.getMap()
                );

        ProcessInstanceTitleVO processInstanceTitleVO = new ProcessInstanceTitleVO();
        processInstanceTitleVO.setProcessInstanceId(processInstance.getProcessInstanceId());
        processInstanceTitleVO.setProcessTitle(startProcessVersionDTO.getBusinessKey());
        return processInstanceTitleVO;
    }

    /**
     * 获取流程的第一个节点的选人列表
     * @param startProcessVersionDTO 流程Key与版本
     * @return 结果
     */
    @Override
    public List<SelectUserVO> getStartProcessUserList(StartProcessVersionDTO startProcessVersionDTO) {
        if(startProcessVersionDTO.getProcessKey() == null || startProcessVersionDTO.getVersion() == null){
            return null;
        }
        ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery()
                .processDefinitionKey(startProcessVersionDTO.getProcessKey())
                .processDefinitionVersion(startProcessVersionDTO.getVersion()).singleResult();
        if(processDefinition.isSuspended()){
            throw new CamundaException("流程已挂起");
        }
        boolean full = judgeFirstUserTaskAssigneeFull(processDefinition.getId());
        if(!full){
            com.tcloudit.common.core.domain.R<List<SelectUserVO>> selectUser = remoteUserService.getSelectUserNotInId(new ArrayList<>());
            if(com.tcloudit.common.core.domain.R.isError(selectUser)){
                throw new RuntimeException("用户服务异常");
            }
            return selectUser.getData();
        }
        return new ArrayList<>();
    }

    /**
     * 获取流程定义的变量
     * @param processInstanceId 流程定义ID
     * @return 结果
     */
    @Override
    public List<ProcessVariableVO> getProcessVariable(String processInstanceId) {
        List<Task> taskList = processEngine.getTaskService().createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();

        List<ProcessVariableVO> processVariableVOList = new ArrayList<>();
        if(taskList.isEmpty()){
            List<HistoricVariableInstance> list = processEngine.getHistoryService().createHistoricVariableInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .list();

            for (HistoricVariableInstance historicVariableInstance : list) {
                ProcessVariableVO processVariableVO = new ProcessVariableVO();
                processVariableVO.setName(historicVariableInstance.getName());
                processVariableVO.setValue(historicVariableInstance.getValue().toString());
                processVariableVOList.add(processVariableVO);
            }
        }
        for (Task task : taskList) {
            Map<String, Object> variables = processEngine.getTaskService().getVariables(task.getId());
            variables.forEach((k, v) -> {
                ProcessVariableVO processVariableVO = new ProcessVariableVO();
                processVariableVO.setName(k);
                processVariableVO.setValue(v.toString());
                processVariableVOList.add(processVariableVO);
            });
        }
        return processVariableVOList;
    }

    /**
     * 获取流程审批日志流程
     * @param processInstanceId 流程定义ID
     * @return 结果
     */
    @Override
    public ProcessCommentLogVO getProcessCommentLog(String processInstanceId) {
        ProcessCommentLogVO processCommentLogVO = new ProcessCommentLogVO();
        // 已完成和正在执行的节点ID
        Set<String> activityIdList = new HashSet<>();

        // 添加已完成的审批操作
        List<HistoricTaskInstance> historicTaskInstanceList = processEngine.getHistoryService()
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished()
                .list();
        List<ProcessCommentVO> processCommentVOS = new ArrayList<>();
        List<ProcessCommentLogVO.ActivityLog> activityLogList = new ArrayList<>();
        ProcessCommentLogVO.ActivityLog activityLog = new ProcessCommentLogVO.ActivityLog();
        for (HistoricTaskInstance historicTaskInstance : historicTaskInstanceList) {
            activityIdList.add(historicTaskInstance.getTaskDefinitionKey());
            if(StringUtils.hasText(activityLog.getId()) &&
                    !activityLog.getId().equals(historicTaskInstance.getTaskDefinitionKey())){
                activityLogList.add(activityLog);
                processCommentVOS = new ArrayList<>();
                activityLog = new ProcessCommentLogVO.ActivityLog();
            }
            activityLog.setId(historicTaskInstance.getTaskDefinitionKey());
            activityLog.setName(historicTaskInstance.getName());
            processCommentVOS.addAll(getComment(historicTaskInstance.getId(), historicTaskInstance.getName()));
            activityLog.setProcessCommentVOS(processCommentVOS);
        }
        if(StringUtils.hasText(activityLog.getId())){
            activityLogList.add(activityLog);
        }
        processCommentLogVO.setActive(activityLogList.size() - 1);

        // 获取正在执行和未来要执行的审核人
        List<Task> taskList = processEngine.getTaskService().createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();

        if(taskList.isEmpty()){
            processCommentLogVO.setActive(activityLogList.size());
            processCommentLogVO.setActivityLogs(activityLogList);
            return processCommentLogVO;
        }

        // 正在执行的审核人
        processCommentVOS = new ArrayList<>();
        activityLog = new ProcessCommentLogVO.ActivityLog();
        Task firstTask = taskList.get(0);
        activityLog.setId(firstTask.getTaskDefinitionKey());
        activityLog.setName(firstTask.getName());
        for (Task task : taskList) {
            ProcessCommentVO commentVO = new ProcessCommentVO();
            commentVO.setUser(task.getAssignee());
            processCommentVOS.add(commentVO);
        }
        if(activityLogList.size() > 0){
            ProcessCommentLogVO.ActivityLog activity = activityLogList.get(activityLogList.size() - 1);
            if(firstTask.getTaskDefinitionKey().equals(activity.getId())){
                List<ProcessCommentVO> commentVOS = activity.getProcessCommentVOS();
                commentVOS.addAll(processCommentVOS);
                activity.setProcessCommentVOS(commentVOS);
            } else {

            }
        }
        activityLog.setProcessCommentVOS(processCommentVOS);
        activityLogList.add(activityLog);
        processCommentLogVO.setActive(activityLogList.size() - 1);

        // 未来要执行的审核人
        activityIdList.add(firstTask.getTaskDefinitionKey());
        ProcessDefinitionEntity processDefinitionEntity =
                (ProcessDefinitionEntity) ((RepositoryServiceImpl) processEngine
                        .getRepositoryService())
                        .getDeployedProcessDefinition(firstTask.getProcessDefinitionId());
        List<ActivityImpl> activities = processDefinitionEntity.getActivities();
        for (ActivityImpl activity : activities) {
            String activityId = activity.getId().replace(CamundaConstant.REPLACE_MULTI_INSTANCE_BODY, "");
            if(firstTask.getTaskDefinitionKey().equals(activityId)){
                setCommentInFuture(activity, activityLogList, activityIdList, firstTask.getProcessDefinitionId());
            }
        }

        processCommentLogVO.setActivityLogs(activityLogList);
        return processCommentLogVO;
    }

    /**
     * 添加未来要执行的审批节点与审批人
     * @param activity 开始节点
     * @param activityLogList 节点日志集合
     * @param activityIdList 已执行或正在执行的节点id集合
     * @param processDefinitionId 流程定义id
     */
    private void setCommentInFuture(
            ActivityImpl activity,
            List<ProcessCommentLogVO.ActivityLog> activityLogList,
            Set<String> activityIdList,
            String processDefinitionId) {
        if(activity == null){
            return;
        }

        String activityId = activity.getId().replace(CamundaConstant.REPLACE_MULTI_INSTANCE_BODY, "");
        if(!activityIdList.contains(activityId)){
            ProcessCommentLogVO.ActivityLog activityLog = new ProcessCommentLogVO.ActivityLog();
            activityLog.setId(activityId);
            activityLog.setName(activity.getName());
            List<ProcessCommentVO> processCommentVOS = new ArrayList<>();
            List<String> assigneeList = new ArrayList<>();
            String type = activity.getProperty(CamundaConstant.TYPE).toString();
            if(CamundaConstant.MULTI_INSTANCE_BODY.equals(type)) {
                List<ActivityImpl> activityList = activity.getActivities();
                for (ActivityImpl ac : activityList) {
                    activityLog.setName(ac.getName());
                }
                List<String> newAssigneeList = getExtensionPropertyByNodeId(
                        processDefinitionId,
                        CamundaConstant.ASSIGNEE,
                        activityId);
                if(newAssigneeList != null && !newAssigneeList.isEmpty()){
                    assigneeList.addAll(newAssigneeList);
                } else {
                    assigneeList.add(CamundaConstant.COMMENT);
                }
            }
            if(CamundaConstant.USER_TASK.equals(type)) {
                UserTaskActivityBehavior activityBehavior = (UserTaskActivityBehavior) activity.getActivityBehavior();
                String expressionText = activityBehavior.getTaskDefinition().getAssigneeExpression().getExpressionText();
                if(!StringUtils.hasText(expressionText)){
                    expressionText = CamundaConstant.COMMENT;
                }
                assigneeList.add(expressionText);
            }
            if(!assigneeList.isEmpty()) {
                for (String assignee : assigneeList) {
                    ProcessCommentVO commentVO = new ProcessCommentVO();
                    commentVO.setUser(assignee);
                    processCommentVOS.add(commentVO);
                }
                activityLog.setProcessCommentVOS(processCommentVOS);
                activityLogList.add(activityLog);
            }
        }

        if(activity.getOutgoingTransitions().size() == 0) {
            return;
        }
        for (PvmTransition outgoingTransition : activity.getOutgoingTransitions()) {
            setCommentInFuture((ActivityImpl) outgoingTransition.getDestination(),
                    activityLogList,
                    activityIdList,
                    processDefinitionId);
        }
    }

    /**
     * 获取流程流转步骤
     * @param processInstanceId 流程定义ID
     * @return 结果
     */
    @Override
    public List<ProcessNodeLogVO> getProcessNodeLog(String processInstanceId) {
        List<ProcessNodeLogVO> processNodeLogVOList = new ArrayList<>();

        List<HistoricActivityInstance> unfinished = processEngine.getHistoryService().createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .unfinished()
                .list();
        String activityId = null;
        for (HistoricActivityInstance activityInstance : unfinished) {
            if(CamundaConstant.USER_TASK.equals(activityInstance.getActivityType())){
                activityId = activityInstance.getActivityId();
            }
        }

        List<HistoricActivityInstance> finished = processEngine.getHistoryService().createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished()
                .list();
        String activityIdTemp = null;
        ProcessNodeLogVO nodeLogVO = null;
        for (HistoricActivityInstance activityInstance : finished) {
            if(!CamundaConstant.USER_TASK.equals(activityInstance.getActivityType())){
                continue;
            }
            if(nodeLogVO == null){
                nodeLogVO = new ProcessNodeLogVO();
                nodeLogVO.setName(activityInstance.getActivityName());
                nodeLogVO.setStartTime(activityInstance.getStartTime());
                if(activityId == null || !activityId.equals(activityInstance.getActivityId())){
                    nodeLogVO.setEndTime(activityInstance.getEndTime());
                }
                activityIdTemp = activityInstance.getActivityId();
            }
            if(!activityInstance.getActivityId().equals(activityIdTemp)){
                processNodeLogVOList.add(nodeLogVO);
                nodeLogVO = new ProcessNodeLogVO();
                activityIdTemp = activityInstance.getActivityId();
                nodeLogVO.setName(activityInstance.getActivityName());
                nodeLogVO.setStartTime(activityInstance.getStartTime());
                if(activityId == null || !activityId.equals(activityInstance.getActivityId())){
                    nodeLogVO.setEndTime(activityInstance.getEndTime());
                }
            } else {
                Date startTime = nodeLogVO.getStartTime();
                Date endTime = nodeLogVO.getEndTime();
                if(startTime != null && startTime.getTime() > activityInstance.getStartTime().getTime()){
                    nodeLogVO.setStartTime(activityInstance.getStartTime());
                }
                if(endTime != null && endTime.getTime() < activityInstance.getEndTime().getTime()){
                    nodeLogVO.setEndTime(activityInstance.getEndTime());
                }
            }
        }
        if(nodeLogVO != null){
            processNodeLogVOList.add(nodeLogVO);
        }
        for (HistoricActivityInstance activityInstance : unfinished) {
            if(CamundaConstant.USER_TASK.equals(activityInstance.getActivityType())){
                ProcessNodeLogVO processNodeLogVO = new ProcessNodeLogVO();
                processNodeLogVO.setName(activityInstance.getActivityName());
                processNodeLogVO.setStartTime(activityInstance.getStartTime());
                processNodeLogVOList.add(processNodeLogVO);
            }
        }

        for (ProcessNodeLogVO processNodeLogVO : processNodeLogVOList) {
            if(processNodeLogVO.getStartTime() != null && processNodeLogVO.getEndTime() != null){
                String spendTime = DateUtils.spendTime(processNodeLogVO.getStartTime(), processNodeLogVO.getEndTime());
                processNodeLogVO.setSpendTime(spendTime);
            }
        }
        return processNodeLogVOList;
    }

    /**
     * 判断第一个用户任务的assignee是否设置了默认分配人
     * 如果没有返回 false
     * @param processDefinitionId 流程定义ID
     * @return 结果
     */
    private boolean judgeFirstUserTaskAssigneeFull(String processDefinitionId){
        ProcessDefinitionEntity processDefinitionEntity =
                (ProcessDefinitionEntity) ((RepositoryServiceImpl) processEngine
                        .getRepositoryService())
                        .getDeployedProcessDefinition(processDefinitionId);
        List<ActivityImpl> activities = processDefinitionEntity.getActivities();

        for (ActivityImpl activity : activities) {
            if(CamundaConstant.START_EVENT.equals(activity.getProperty(CamundaConstant.TYPE))){
                ActivityImpl userTask = returnFirstUserTask(activity);
                if(userTask == null){
                    throw new CamundaException("流程模型没有用户任务");
                }
                if(CamundaConstant.USER_TASK.equals(userTask.getProperty(CamundaConstant.TYPE))){
                    UserTaskActivityBehavior activityBehavior = (UserTaskActivityBehavior) userTask.getActivityBehavior();
                    String expressionText = activityBehavior.getTaskDefinition().getAssigneeExpression().getExpressionText();
                    if(!StringUtils.hasText(expressionText)){
                        throw new CamundaException("流程第一个用户任务节点没有设置分配人");
                    }
                }
                if(CamundaConstant.MULTI_INSTANCE_BODY.equals(userTask.getProperty(CamundaConstant.TYPE))){
                    List<String> assigneeList = getExtensionPropertyByNodeId(
                            processDefinitionId,
                            CamundaConstant.ASSIGNEE,
                            userTask.getId().replace(CamundaConstant.REPLACE_MULTI_INSTANCE_BODY, ""));
                    if(assigneeList == null || assigneeList.isEmpty()){
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * 递归查找下一个节点是否为用户任务，如是则返回，否则继续
     * @param activity 活动实例
     * @return 结果
     */
    private ActivityImpl returnFirstUserTask(ActivityImpl activity){
        List<PvmTransition> outgoingTransitions = activity.getOutgoingTransitions();
        for (PvmTransition outgoingTransition : outgoingTransitions) {
            ActivityImpl outActivity = (ActivityImpl) outgoingTransition.getDestination();
            if(outActivity == null){
                return null;
            }
            if(
                    CamundaConstant.USER_TASK.equals(outActivity.getProperty(CamundaConstant.TYPE)) ||
                    CamundaConstant.MULTI_INSTANCE_BODY.equals(outActivity.getProperty(CamundaConstant.TYPE))
            ){
                return outActivity;
            } else {
                return returnFirstUserTask(outActivity);
            }
        }
        return null;
    }

    /**
     * 获取审核意见
     * @param taskId 任务ID
     * @param taskName 任务名称
     * @return 结果
     */
    private List<ProcessCommentVO> getComment(String taskId, String taskName) {
        List<ProcessCommentVO> processCommentVOS = new ArrayList<>();
        List<Comment> comments = processEngine
                .getTaskService()
                .getTaskComments(taskId);
        comments.sort(Comparator.comparing(Comment::getTime));
        for (Comment comment : comments) {
            ProcessCommentVO commentVO = new ProcessCommentVO();
            commentVO.setName(taskName);
            commentVO.setUser(comment.getUserId());

            String fullMessage = comment.getFullMessage();
            String[] split = fullMessage.split(CamundaConstant.OPINION_SPLIT);
            commentVO.setHandler(split[0]);

            commentVO.setResolution(fullMessage.replace(split[0] + ";", ""));
            commentVO.setEndTime(comment.getTime());
            processCommentVOS.add(commentVO);
        }
        return processCommentVOS;
    }

    /**
     * 规则判断，根据用户任务节点的外部属性规则判断
     * 如满足同意人数，则进入下一节点
     * 如满足拒绝人数，则退回到开始节点
     * @param task 任务
     * @param full 同意 ture 拒绝 false
     * @param opinion 审批意见
     * @return 布尔值
     */
    private Boolean returnStartNode(Task task, Boolean full, String opinion, Map<String, Object> formMap){
        String taskId = task.getId();
        String processInstanceId = task.getProcessInstanceId();
        String taskDefinitionKey = task.getTaskDefinitionKey();
        String startUserTaskActivity = null;
        String assignee = null;


        List<HistoricActivityInstance> instances = processEngine.getHistoryService().createHistoricActivityInstanceQuery()
                .activityType(CamundaConstant.USER_TASK)
                .processInstanceId(task.getProcessInstanceId())
                .finished()
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .list();

        for(HistoricActivityInstance activityInstance : instances){
            startUserTaskActivity = activityInstance.getActivityId();
            assignee = activityInstance.getAssignee();
            break;
        }

        Integer success = 0;
        Integer error = 0;
        if(full){
            success++;
        }else{
            error++;
        }
        List<String> taskIdList = processEngine.getHistoryService().createHistoricTaskInstanceQuery()
                .finished()
                .processInstanceId(processInstanceId)
                .taskDefinitionKey(taskDefinitionKey)
                .list()
                .stream().map(HistoricTaskInstance::getId)
                .collect(Collectors.toList());
        for (String id : taskIdList) {
            List<Comment> comments = processEngine.getTaskService().getTaskComments(id);
            for (Comment comment : comments) {
                String fullMessage = comment.getFullMessage().split(CamundaConstant.OPINION_SPLIT)[0];
                if(CamundaConstant.SUCCESS.equals(fullMessage)){
                    success ++;
                }
                if(CamundaConstant.ERROR.equals(fullMessage)){
                    error ++;
                }
            }
        }

        Integer nrOfInstance = (Integer) processEngine.getRuntimeService()
                .getVariable(task.getExecutionId(), CamundaConstant.NR_OF_INSTANCES);

        List<String> conditionList = getCurrentNodeExtensionProperty(task, CamundaConstant.CONDITION);
        Map<String, Object> map = new HashMap<>();
        map.put(CamundaConstant.SUCCESS_ATTR, success);
        map.put(CamundaConstant.ERROR_ATTR, error);
        map.put(CamundaConstant.NR_OF_INSTANCES, nrOfInstance);
        for (String condition : conditionList) {
            if(CamundaUtils.isCondition(map, condition)){
                if(condition.contains(CamundaConstant.SUCCESS_ATTR)){
                    Map<String, Object> variables = processEngine.getRuntimeService().getVariables(task.getExecutionId());
                    variables.put(
                            CamundaConstant.NR_OF_COMPLETED_INSTANCES,
                            nrOfInstance - 1);
                    variables.putAll(formMap);
                    processEngine.getTaskService().complete(taskId, variables);
                    addOpinion(
                            taskId,
                            processInstanceId,
                            CamundaConstant.SUCCESS + CamundaConstant.OPINION_SPLIT + opinion);
                    return true;
                }
                if(condition.contains(CamundaConstant.ERROR_ATTR)) {
                    processEngine.getRuntimeService().createProcessInstanceModification(processInstanceId)
                            .cancelAllForActivity(taskDefinitionKey)
                            .startBeforeActivity(startUserTaskActivity)
                            .setVariable(CamundaConstant.ASSIGNEE, assignee)
                            .execute();
                    addOpinion(
                            taskId,
                            processInstanceId,
                            CamundaConstant.ERROR + CamundaConstant.OPINION_SPLIT + opinion);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断下一个节点是否还有用户任务
     * 有：判断是否设置了默认用户
     * 没有：删除 map 中 assigneeList 参数
     * @param map 参数
     * @param task 任务
     */
    private void judgeNextNodeAssigneeExist(Map<String, Object> map, Task task){
        if(map == null){
            map = new HashMap<>();
        }

        // 判断下一个用户任务节点是否是串行或并行，是的话就判断外部属性 assignee 是否有值
        // false 没有值：判断 map 里是否有 assigneeList 而且是否有值，没有则抛出异常
        // true 有值：删除 map 里的 assigneeList
        List<String> nextNodeAssigneeList = getFlowNextNode(task);
        boolean can = false;
        if(nextNodeAssigneeList != null){
             can = true;
        }

        if(can){
            boolean containsKey = map.containsKey(CamundaConstant.ASSIGNEE_LIST);
            if(containsKey){
                map.remove(CamundaConstant.ASSIGNEE_LIST);
            }
        } else {
            Object object = map.get(CamundaConstant.ASSIGNEE_LIST);
            List<String> assigneeList = CamundaUtils.ObjectChangeArrays(object);
            if(assigneeList.size() == 0){
                throw new RuntimeException("没有选中用户");
            }
        }
    }

    /**
     * 根据当前任务获取当前的用户任务
     * 得到扩展属性 assignee
     * @param task 任务
     */
    private List<String> getFlowNextNode(Task task){
        ProcessDefinitionEntity processDefinitionEntity =
                (ProcessDefinitionEntity) ((RepositoryServiceImpl) processEngine
                        .getRepositoryService())
                        .getDeployedProcessDefinition(task.getProcessDefinitionId());
        String activityId = task.getTaskDefinitionKey();
        List<ActivityImpl> activities = processDefinitionEntity.getActivities();
        Map<String, Object> variables = processEngine.getRuntimeService().getVariables(task.getExecutionId());

        for (ActivityImpl activity : activities) {
            if(activity.getId().replace(CamundaConstant.REPLACE_MULTI_INSTANCE_BODY, "").equals(activityId)){
                return getExtensionPropertyByFlowNextUserTaskNode(activity, task.getProcessDefinitionId(), variables);
            }
        }
        return null;
    }

    /**
     * 获取下一个节点直到获取到用户任务节点，取出任务节点的外部扩展属性
     * @param activity 节点
     * @param processDefinitionId 流程定义ID
     * @param variables 参数
     * @return 外部扩展属性 assignee
     */
    private List<String> getExtensionPropertyByFlowNextUserTaskNode(
            ActivityImpl activity,
            String processDefinitionId,
            Map<String, Object> variables){
        String id = activity.getId();
        List<PvmTransition> outgoingTransitions = activity.getOutgoingTransitions();
        for (PvmTransition outgoingTransition : outgoingTransitions) {
            ActivityImpl outActivity = (ActivityImpl) outgoingTransition.getDestination();
            String type = (String) outActivity.getProperty(CamundaConstant.TYPE);
            if(CamundaConstant.MULTI_INSTANCE_BODY.equals(type) && !outActivity.getId().equals(id)){
                String newNodeId = outActivity.getId().replace(CamundaConstant.REPLACE_MULTI_INSTANCE_BODY, "");
                return getExtensionPropertyByNodeId(
                        processDefinitionId,
                        CamundaConstant.ASSIGNEE,
                        newNodeId);
            } else if (CamundaConstant.EXCLUSIVE_GATEWAY.equals(type)
                    || CamundaConstant.INCLUSIVE_GATEWAY.equals(type)) {
                List<PvmTransition> transitions = outActivity.getOutgoingTransitions();
                if(transitions.size() == 1){
                    return getExtensionPropertyByFlowNextUserTaskNode(outActivity, processDefinitionId, variables);
                }
                if(transitions.size() > 1){
                    for (PvmTransition transition : transitions) {
                        String property = (String) transition.getProperty(CamundaConstant.CONDITION_TEXT);
                        if(StringUtils.hasText(property)){
                            if(CamundaUtils.isCondition(variables, property)){
                                return getExtensionPropertyByFlowNextUserTaskNode(outActivity, processDefinitionId, variables);
                            }
                        }
                    }
                }
            } else if (CamundaConstant.PARALLEL_GATEWAY.equals(type)){
                return getExtensionPropertyByFlowNextUserTaskNode(outActivity, processDefinitionId, variables);
            } else {
                return new ArrayList<>();
            }
        }
        return new ArrayList<>();
    }

    /**
     * 判断这个节点是普通节点还是并行或串行节点
     * 并行、串行 ： true
     * 普通 ： false
     * @param processDefinitionId 流程定义ID
     * @param nodeId 节点ID
     * @return 结果
     */
    private Boolean isMultiInstance(String processDefinitionId, String nodeId){
        ModelElementInstance modelElementById = processEngine
                .getRepositoryService()
                .getBpmnModelInstance(processDefinitionId)
                .getModelElementById(nodeId);
        LoopCharacteristics loopCharacteristics = ((UserTask) modelElementById).getLoopCharacteristics();
        if(loopCharacteristics == null){
            return false;
        }
        return true;
    }

    /**
     * 判断这个节点是并行还是串行
     * 串行 ： true
     * 并行 ： false
     * @param processDefinitionId 流程定义ID
     * @param nodeId 节点ID
     * @return 结果
     */
    private Boolean isMultiInstanceSequential(String processDefinitionId, String nodeId){
        ModelElementInstance modelElementById = processEngine
                .getRepositoryService()
                .getBpmnModelInstance(processDefinitionId)
                .getModelElementById(nodeId);
        LoopCharacteristics loopCharacteristics = ((UserTask) modelElementById).getLoopCharacteristics();
        if(loopCharacteristics == null){
            throw new RuntimeException("任务异常");
        }

        // 判断节点是并行还是串行
        String isSequential = loopCharacteristics.getDomElement().getAttribute("isSequential");
        return isSequential.equals(CamundaConstant.TRUE);

    }

    /**
     * 获取节点对应的扩展属性
     * @param processDefinitionId 流程定义ID
     * @param propertyName 扩展属性名
     * @param nodeId 节点ID
     * @return 结果
     */
    private List<String> getExtensionPropertyByNodeId(String processDefinitionId, String propertyName, String nodeId){
        InputStream processModel = processEngine.getRepositoryService().getProcessModel(processDefinitionId);
        UserTask userTask = Bpmn.readModelFromStream(processModel).getModelElementById(nodeId);
        Collection<CamundaProperty> camundaProperties = userTask.getExtensionElements().getElementsQuery()
                .filterByType(CamundaProperties.class)
                .singleResult()
                .getCamundaProperties();
        for (CamundaProperty camundaProperty : camundaProperties) {
            if(propertyName.equals(camundaProperty.getCamundaName())){
                String camundaValue = camundaProperty.getCamundaValue();
                if(camundaValue != null){
                    String[] split = camundaValue.split(CamundaConstant.ASSIGNEE_SPLIT);
                    return Arrays.asList(split);
                }
            }
        }
        return null;
    }

    /**
     * 获取当前节点得到对应的扩展属性
     * @param task 任务
     * @param propertyName 扩展属性名
     * @return 结果
     */
    private List<String> getCurrentNodeExtensionProperty(Task task, String propertyName){
        InputStream processModel = processEngine.getRepositoryService().getProcessModel(task.getProcessDefinitionId());
        UserTask userTask = Bpmn.readModelFromStream(processModel).getModelElementById(task.getTaskDefinitionKey());
        Collection<CamundaProperty> camundaProperties = userTask.getExtensionElements().getElementsQuery()
                .filterByType(CamundaProperties.class)
                .singleResult()
                .getCamundaProperties();
        for (CamundaProperty camundaProperty : camundaProperties) {
            if(propertyName.equals(camundaProperty.getCamundaName())){
                String camundaValue = camundaProperty.getCamundaValue();
                if(camundaValue != null){
                    String[] split = camundaValue.split(CamundaConstant.ASSIGNEE_SPLIT);
                    return Arrays.asList(split);
                }
            }
        }
        return new ArrayList<>();
    }

    /**
     * 读取字节流
     * @param inputStream 字节流
     * @return 结果
     */
    private String readInputStream(InputStream inputStream){
        byte[] buffer = new byte[16*1024];
        StringBuilder stringBuffer = new StringBuilder();
        int read = 0;
        try {
            read = inputStream.read(buffer);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        stringBuffer.append(new String(buffer, 0, read));
        return stringBuffer.toString();
    }

    /**
     * 获取用户的名称加角色
     * @return 结果
     */
    public String getJoinUserPostName(){
        com.tcloudit.common.core.domain.R<UserTO> userTO = remoteUserService.getUserIdDeptName(SecurityUtils.getUserId().toString());
        if(com.tcloudit.common.core.domain.R.isError(userTO)){
            return null;
        }
        UserTO data = userTO.getData();
        return ProcessUserUtils.joinUserPost(SecurityUtils.getUserId().toString(), data);
    }

    /**
     * 判断加签、减签等操作参数是否正确，是否具有权限
     * @param signDTO 参数
     * @throws RuntimeException 不正确就抛出异常
     */
    private void isAttrRight(SignDTO signDTO, Task task, String handleName) throws RuntimeException{
        List<String> userId = signDTO.getUserId();
        if(!(userId.size() > 0)){
            throw new RuntimeException("没有选中用户");
        }

        String taskDefinitionKey = task.getTaskDefinitionKey();
        String processInstanceId = task.getProcessInstanceId();
        if(!isAuth(handleName, processInstanceId, taskDefinitionKey)){
            throw new RuntimeException("没有权限");
        }
    }

    /**
     * 判断当前执行的节点是否具有权限
     * @param doName 权限名
     * @param processInstanceId 流程实例ID
     * @param nodeId 节点ID
     * @return 结果
     */
    private Boolean isAuth(String doName, String processInstanceId, String nodeId){
        ProcessInstance processInstance = processEngine.getRuntimeService()
                .createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .active()
                .singleResult();
        if(processInstance == null){
            return false;
        }
        String processDefinitionId = processInstance.getProcessDefinitionId();
        ModelElementInstance modelElementById = processEngine.getRepositoryService()
                .getBpmnModelInstance(processDefinitionId)
                .getModelElementById(nodeId);

        Collection<CamundaProperty> camundaProperties = ((UserTask) modelElementById)
                .getExtensionElements()
                .getElementsQuery()
                .filterByType(CamundaProperties.class)
                .singleResult()
                .getCamundaProperties();
        for (CamundaProperty camundaProperty : camundaProperties) {
            if(CamundaConstant.AUTH.equals(camundaProperty.getCamundaName())){
                String camundaValue = camundaProperty.getCamundaValue();
                if(camundaValue != null){
                    List<String> stringList = Arrays.asList(camundaValue.split(CamundaConstant.OPINION_SPLIT));
                    if(stringList.contains(doName)){
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * 添加审批意见
     * @param taskId 任务ID
     * @param processInstanceId 流程定义ID
     * @param opinion 审批意见
     */
    private void addOpinion(String taskId, String processInstanceId, String opinion){
        String joinUserPostName = getJoinUserPostName();
        processEngine.getIdentityService().setAuthenticatedUserId(joinUserPostName);
        processEngine.getTaskService().createComment(taskId,
                processInstanceId,
                opinion);
    }
}
